/*
 *  $Id: Player.cs 1233 2010-12-18 17:54:48Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;
using System.Text;

using AgateLib.DisplayLib;
using AgateLib.Geometry;
using AgateLib.InputLib;

using Ragima.Helper;

namespace Ragima {

    /// <summary>
    /// Фазы игры
    /// </summary>
    public enum PlayerState {
        Defence,
        Offence,
    }

    /// <summary>
    /// Игрок
    /// </summary>
    public class Player: AbstractObject, ITabSource {
        private Game _game;
        private Player _enemy;
        private PlayerInfo _info;
        private PlayerState _state;
        private GameCardDeck _deck, _deck2;
        private Graveyard _grave;
        private List<HandCard> _hands;
        private ElementValues _mana, _manaStep;
        private bool _hasDecMana;
        private bool _hasMeditation;
        private int _counterMeditation, _meditation;
        private Creature[] _creatures;
        private List<Land> _lands;
        private AffectSummary _globalSummary;
        private List<Region> _regions;
        private Player _selectedPlayer;
        private Creature _selectedCreature;
        private Land _selectedLand;
        private Tabs _tabs, _newTabs;
        private TabType _defaultTab;
        private int _healthPos;

        public Player(Game game, PlayerInfo info) : base() {
            _game = game;
            _info = info;
            _state = PlayerState.Defence;
            _deck = new GameCardDeck(info.Deck);
            _deck2 = new GameCardDeck();
            _grave = new Graveyard();
            _hands = new List<HandCard>();
            _mana = new ElementValues(Constants.DefaultMana);
            _manaStep = new ElementValues();
            _hasDecMana = false;
            _hasMeditation = false;
            _counterMeditation = 0;
            _meditation = 0;
            _creatures = new Creature[Constants.FieldSize];
            _lands = new List<Land>();
            _globalSummary = new AffectSummary();
            _selectedPlayer = this;
            Health = MaxHealth;
            _defaultTab = TabType.Cards;
            _healthPos = 0;
            SetPlayerAbilities(Summary);
            foreach (ElementType element in ElementType.ManaElements)
                GlobalSummary.ChangeManaIncome(element, 1);
            GlobalSummary.ChangeManaIncome(info.Deck.MainElement, 1);
        }

        private void SetPlayerAbilities(ICollector<AbilityCard> abilities) {
            MainDeck.GetPlayerAbilities(abilities);
        }

        public Game Game {
            get { return _game; }
        }

        public bool IsMaster {
            get { return _game.MasterPlayer == this; }
        }
        public bool IsCurrent {
            get { return _game.CurrentPlayer == this; }
        }

        /// <value>
        /// Владелец игрока = сам игрок
        /// </value>
        public override Player Owner {
            get { return this; }
        }

        /// <value>
        /// Имя игрока
        /// </value>
        public override Languages.Names Name { get { return _info.Name; } }

        /// <value>
        /// Аватар игрока
        /// </value>
        public Surface Avatar { get { return _info.Avatar; } }

        /// <value>
        /// Вражеский игрок
        /// </value>
        public Player Enemy {
            get { return _enemy; }
            set { _enemy = value; }
        }

        /// <value>
        /// Фаза игры, в которой находится игрок
        /// </value>
        public PlayerState State { get { return _state; } }

        /// <value>
        /// Текущее здоровье игрока
        /// </value>
        public override int Health {
            get {
                return Math.Max(_mana[ElementType.Health] + Summary.GetMaxHealth(), MinHealth);
            }
            set {
                _mana[ElementType.Health] = value - Summary.GetMaxHealth();
            }
        }

        /// <value>
        /// Минимальное здоровье игрока
        /// </value>
        public override int MinHealth {
            get {
                return Summary.GetMinHealth();
            }
        }

        /// <value>
        /// Максимальное здоровье игрока
        /// </value>
        public override int MaxHealth {
            get {
                return Math.Max(Constants.DefaultPlayerHealth + Summary.GetMaxHealth(), 0);
            }
        }

        public override void InstantDeath() {
            Health = MinHealth;
        }

        /// <value>
        /// Текущие табы игрока
        /// </value>
        public Tabs Tabs { get { return _tabs; } }
        //// <value>
        /// Открываемый по умолчанию таб
        /// </value>
        public TabType DefaultTab { get { return _defaultTab; } set { _defaultTab = value; } }

        public GameCardDeck MainDeck { get { return _deck; } }
        public GameCardDeck SecondDeck { get { return _deck2; } }
        public List<HandCard> Hands { get { return _hands; } }
        public List<Land> Lands { get { return _lands; } }
        public Graveyard Graveyard { get { return _grave; } }

        public override AffectSummary GlobalSummary {
            get { return _globalSummary; }
        }

        public int Meditation {
            get { return _meditation; }
        }
        public int MeditationCount {
            get { return _counterMeditation; }
        }

        public bool TestCurrent(bool notice) {
            if (!IsCurrent) {
                if (notice) Game.ErrorNotice(Resources.Game.NotYourTurn.GetText());
                return false;
            }
            return true;
        }

        /// <summary>
        /// Переместить одну карту из колоды в руки
        /// </summary>
        public void CardToHands(bool opened, bool logged) {
            Data.Sounds.Events.Play("card.take");
            if (_deck.Count == 0) {
                if (_deck2.Count == 0) return;
                // если колода пуста, а во второй что-то есть, то берем все из второй и перемешиваем
                _deck2.MoveTo(_deck);
                _deck.Shuffle(Game.Current.Random);
            }
            if (logged) Game.Log.Add(Resources.Game.GetCard, this);
            _hands.Add(new HandCard(opened, _deck.GetCard()));
        }

        /// <summary>
        /// Поместить указанные карты в руки
        /// </summary>
        public void CardToHands(bool opened, bool logged, params Card[] cards) {
            if (cards.Length == 0) return;
            Data.Sounds.Events.Play("card.take");
            if (logged) Game.Log.Add(Resources.Game.GetCards, this, cards.Length);
            foreach (Card card in cards)
                _hands.Add(new HandCard(opened, card));
        }

        /// <summary>
        /// Сбросить несколько карт
        /// </summary>
        /// <param name="cards">Сбрасываемые карты</param>
        public void DropCards(params HandCard[] cards) {
            if (cards.Length == 0) return;
            Game.Log.Add(Resources.Game.LoseCard, this, cards.Length);
            foreach (HandCard card in cards)
                if (_hands.Contains(card)) {
                    _deck2.Add(card.Card);
                    _hands.Remove(card);
                }
        }

        /// <summary>
        /// Сбросить некоторое число карт
        /// </summary>
        /// <param name="count">Число теряемых карт</param>
        public void ReleaseCards(int count) {
            if (count > _hands.Count) count = _hands.Count;
            if (count == 0) return;
            Game.Current.Log.Add(Resources.Game.LoseCard, this, count);
            while (count > 0) {
                _deck2.Add(_hands[0].Card);
                _hands.RemoveAt(0);
                count--;
            }
        }

        /// <summary>
        /// Переместить начальные карты из колоды в руки
        /// </summary>
        public void StartCardsToHands() {
            for (int i = 0; i < Constants.StartCardCount; i++) CardToHands(true, false);
        }

        public Player SelectedPlayer {
            get { return _selectedPlayer; }
        }
        public Creature SelectedCreature {
            get { return _selectedCreature; }
        }
        public Land SelectedLand {
            get { return _selectedLand; }
        }
        public AbstractObject SelectedObject {
            get { return _selectedCreature ?? _selectedLand ?? _selectedPlayer ?? (AbstractObject) this; }
            set {
                if (value == null)
                    ResetSelection();
                else if (value is Creature)
                    SelectCreature(value as Creature);
                else if (value is Land)
                    SelectLand(value as Land);
                else if (value is Player)
                    SelectPlayer(value as Player);
            }
        }

        public Creature GetCreature(int i) {
            return (i >= 0 && i < _creatures.Length) ? _creatures[i] : null;
        }
        public Creature GetCreature(Predicate<Creature> predicate) {
            for (int i = 0; i < Constants.FieldSize; i++)
                if (_creatures[i] != null && predicate(_creatures[i])) return _creatures[i];
            return null;
        }
        public int GetCreatureCount() {
            int count = 0;
            for (int i = 0; i < Constants.FieldSize; i++)
                if (_creatures[i] != null) count++;
            return count;
        }
        public int GetAttackerCount() {
            int count = 0;
            for (int i = 0; i < Constants.FieldSize; i++)
                if (_creatures[i] != null && _creatures[i].State == CreatureState.Offence) count++;
            return count;
        }
        public IEnumerable<Creature> GetCreatures() {
            for (int i = 0; i < Constants.FieldSize; i++)
                if (_creatures[i] != null) yield return _creatures[i];
        }
        public IEnumerable<Creature> GetAttackers() {
            for (int i = 0; i < Constants.FieldSize; i++) {
                Creature attacker = _creatures[i];
                if (attacker != null && attacker.State == CreatureState.Offence)
                    yield return attacker;
            }
        }
        public IEnumerable<Creature> GetBlockedAttackers(Creature defender) {
            for (int i = 0; i < Constants.FieldSize; i++) {
                Creature attacker = _creatures[i];
                if (attacker != null && attacker.State == CreatureState.Offence && defender.CanBlock(attacker))
                    yield return attacker;
            }
        }

        public int GetFreePlaceCount() {
            int count = 0;
            for (int i = 0; i < Constants.FieldSize; i++)
                if (_creatures[i] == null) count++;
            return count;
        }

        public int GetRandomFreePlace() {
            if (GetFreePlaceCount() == 0) return -1;
            int i;
            do {
                i = Game.Current.Random.Next(Constants.FieldSize);
            } while (_creatures[i] != null);
            return i;
        }

        public int GetNearestFreePlace(int from) {
            if (from < 0 || from >= Constants.FieldSize) return -1;
            if (IsFreePlace(from)) return from;
            int steps = Math.Max(from, Constants.FieldSize-1-from);
            for (int i = 1; i <= steps; i++) {
                if (IsFreePlace(from-i)) return from-i;
                if (IsFreePlace(from+i)) return from+i;
            }
            return -1;
        }

        private bool IsFreePlace(int place) {
            if (place < 0 || place >= Constants.FieldSize) return false;
            return _creatures[place] == null;
        }

        public IEnumerable<Land> GetLandsWithoutAntimagic() {
            foreach (Land land in Lands)
                if (!land.HasFlag<Affects.AntimagicArea>())
                    yield return land;
        }

        public void SelectPlayer(Player player) {
            _selectedPlayer = player;
            _selectedCreature = null;
            _selectedLand = null;
            if (Game.HasTarget && !Game.Target.IsRealTarget)
                Game.ResetTarget();
            Game.UpdateAll();
        }
        public void SelectCreature(Creature creature) {
            if (creature == null) {
                ResetSelection();
                return;
            }
            _selectedPlayer = this;
            _selectedCreature = creature;
            _selectedLand = null;
            Game.UpdateAll();
        }
        public void SelectLand(Land land) {
            if (land == null) {
                ResetSelection();
                return;
            }
            _selectedPlayer = this;
            _selectedCreature = null;
            _selectedLand = land;
            Game.UpdateAll();
        }
        public void ResetSelection() {
            DefaultTab = TabType.Cards;
            SelectPlayer(this);
        }

        public void DoUse(HandCard hCard, TargetArgs args) {
            if (hCard == null || !_hands.Contains(hCard)) return;
            Card card = hCard.Card;
            _hands.Remove(hCard);
            Game.UpdateAll();
            DoUseCard(card, args);
            GlobalSummary.DoUseCard(card, args);
        }

        public void DoUse(AbilityCard ability, TargetArgs args) {
            DoUseCard(ability, args);
            args.Caster.OnAfterUseAbility(ability, args);
        }

        private void DoUseCard(Card card, TargetArgs args) {
            card.LogAction(Game.Log, args);
            card.DoConditionsFor(args);
            card.DoAction(args);
            CardPutTo(card, card.PlaceAfterUse);
            Game.UpdateAll();
            HandleChanges();
        }

        private void HandleChanges() {
            Summary.DoUpdateAura();
            GlobalSummary.DoUpdateAura();
            EnemySummary.DoUpdateAura();
            Game.HealthChange();
            Game.DoDeath();
        }

        public Creature SummonCreature(CreatureCard card, int place) {
            Creature creature = new Creature(this, card);
            SummonCreature(creature, place);
            return creature;
        }

        public Creature ResurrectCreature(GraveyardItem graveCard, int place, bool logged) {
            if (!Graveyard.Contains(graveCard)) return null;
            CreatureCard card = graveCard.Card;
            Graveyard.Remove(graveCard);
            if (logged) Game.Current.Log.Add(Resources.Game.Resurrect, this, card);
            Creature creature = new Creature(this, card);
            graveCard.RestoreCreature(creature);
            creature.AddAffect<Affects.Resurrect>();
            SummonCreature(creature, place);
            return creature;
        }

        public Creature ResurrectCreatureToPlayer(Player player, GraveyardItem graveCard, int place, bool logged) {
            if (!Graveyard.Contains(graveCard)) return null;
            CreatureCard card = graveCard.Card;
            Graveyard.Remove(graveCard);
            Creature creature = new Creature(this, card);
            creature.SetOwner(player);
            graveCard.RestoreCreature(creature);
            creature.AddAffect<Affects.Resurrect>();
            player.SummonCreature(creature, place);
            return creature;
        }

        public void DoUndying() {
            while (GetFreePlaceCount() > 0) {
                GraveyardItem graveCard = Graveyard.GetUndying();
                if (graveCard == null) break;
                int place = GetRandomFreePlace();
                Game.Current.Log.Add(Resources.Game.ResurrectSelf, graveCard.Card);
                ResurrectCreature(graveCard, place, false);
            }
        }

        public void SummonCreature(Creature creature, int place) {
            if (place < 0 || place >= _creatures.Length) return;
            if (_creatures[place] != null) _creatures[place].Remove();
            creature.Number = place;
            _creatures[place] = creature;
            Game.UpdateAll();
            creature.SetSummonState();
            creature.OnSummon();
            HandleChanges();
        }

        public void AddCreature(Creature creature, int place) {
            if (place < 0 || place >= _creatures.Length) return;
            if (_creatures[place] != null) _creatures[place].Remove();
            creature.Number = place;
            _creatures[place] = creature;
            creature.SetAffects();
            Game.UpdateAll();
            HandleChanges();
        }

        public void SummonLand(LandCard card) {
            Land land = new Land(this, card);
            _lands.Add(land);
            Game.UpdateAll();
            land.OnSummon();
            HandleChanges();
        }

        public void AddLand(Land land) {
            _lands.Add(land);
            land.SetAffects();
            Game.UpdateAll();
            HandleChanges();
        }

        public void RemoveCreature(Creature creature, bool events, bool death) {
            if (this != creature.Owner) return;
            _creatures[creature.Number] = null;
            switch (creature.State) {
            case CreatureState.Offence:
                UnsetAttack(creature, false);
                break;
            case CreatureState.Defence:
                UnsetDefence(creature);
                break;
            }
            if (events) {
                if (death) creature.OnDeath();
                creature.MoveCardAfterDeath();
                creature.RemoveAffects();
            } else
                creature.ResetAffects();
            HandleChanges();
        }

        public void RemoveLand(Land land, bool events) {
            if (this != land.Owner) return;
            _lands.Remove(land);
            if (events) {
                land.OnDeath();
                land.MoveCardAfterDeath();
                land.RemoveAffects();
            } else
                land.ResetAffects();
            HandleChanges();
        }

        public void MoveCreature(Creature creature, int to) {
            if (creature == null || creature.State != CreatureState.Untapped || _creatures[to] != null) return;
            Game.Log.Add(Resources.Game.Move, creature);
            int from = creature.Number;
            _creatures[to] = creature;
            _creatures[from] = null;
            creature.Number = to;
            creature.SetMoveState();
            creature.OnMove(from, to);
            ResetSelection();
            HandleChanges();
        }

        public void SetAttack(Creature creature, bool logged) {
            if (this != creature.Owner || creature.State != CreatureState.Untapped) return;
            bool canAttack = true;
            creature.Summary.DoAttackTry(creature, ref canAttack);
            GlobalSummary.DoAttackTry(creature, ref canAttack);
            if (!canAttack) return;
            if (logged) Game.Log.Add(Resources.Game.AttackReady, creature);
            creature.State = CreatureState.Offence;
            ResetSelection();
            HandleChanges();
        }

        public void UnsetAttack(Creature creature, bool logged) {
            if (this != creature.Owner || creature.State != CreatureState.Offence) return;
            if (logged) Game.Log.Add(Resources.Game.AttackCancel, creature);
            creature.State = CreatureState.Untapped;
            ResetSelection();
            HandleChanges();
        }

        public void ResetAttack() {
            for (int i = 0; i < Constants.FieldSize; i++) {
                Creature creature = _creatures[i];
                if (creature != null && creature.State == CreatureState.Offence) {
                    creature.SetAfterAttackState();
                    creature.Defenders.Clear();
                }
            }
            HandleChanges();
        }

        public void SetDefence(Creature defender, Creature attacker, bool logged) {
            if (this != defender.Owner || defender.State != CreatureState.Untapped) return;
            if (Enemy != attacker.Owner || attacker.State != CreatureState.Offence) return;
            bool canDefence = true;
            defender.Summary.DoDefenceTry(defender, attacker, ref canDefence);
            GlobalSummary.DoDefenceTry(defender, attacker, ref canDefence);
            if (!canDefence) return;
            if (logged) Game.Log.Add(Resources.Game.DefendReady, defender);
            defender.State = CreatureState.Defence;
            attacker.Defenders.Add(defender);
            ResetSelection();
            HandleChanges();
        }
        public void SetDefence(Creature defender, List<Creature> attackers, bool logged) {
            if (this != defender.Owner || defender.State != CreatureState.Untapped) return;
            int defCount = 0;
            foreach (Creature attacker in attackers) {
                bool canDefence = true;
                defender.Summary.DoDefenceTry(defender, attacker, ref canDefence);
                GlobalSummary.DoDefenceTry(defender, attacker, ref canDefence);
                if (canDefence) {
                    attacker.Defenders.Add(defender);
                    defCount++;
                }
            }
            if (defCount > 0) {
                if (logged) Game.Log.Add(Resources.Game.DefendReady, defender);
                defender.State = CreatureState.Defence;
                ResetSelection();
                HandleChanges();
            }
        }
        public int GetDefencesCount(Creature defender) {
            int count = 0;
            foreach (Creature attacker in Enemy.GetAttackers())
                if (attacker.Defenders.Contains(defender)) count++;
            return count;
        }

        public void UnsetDefence(Creature defender) {
            defender.State = CreatureState.Untapped;
            foreach (Creature attacker in Enemy.GetAttackers())
                attacker.Defenders.Remove(defender);
            ResetSelection();
            HandleChanges();
        }
        public void UnsetDefence(Creature defender, Creature attacker) {
            if (GetDefencesCount(defender) < 2)
                UnsetDefence(defender);
            else {
                attacker.Defenders.Remove(defender);
                ResetSelection();
            }
            HandleChanges();
        }

        public void ResetDefence() {
            foreach (Creature creature in GetCreatures())
                if (creature.State == CreatureState.Defence)
                    creature.State = CreatureState.Untapped;
            HandleChanges();
        }

        public void CardPutTo(Card card, CardPlace place) {
            if (card.MaxCount == 0) return;
            switch (place) {
                case CardPlace.Hands:
                    _hands.Add(new HandCard(true, card));
                    break;
                case CardPlace.Deck:
                    _deck.Add(card);
                    break;
                case CardPlace.Deck2:
                    _deck2.Add(card);
                    break;
            }
        }
        public void CardPutTo(Creature creature, CardPlace place) {
            if (creature.Card.MaxCount == 0) return;
            switch (place) {
                case CardPlace.Grave:
                    _grave.Add(creature);
                    break;
                default:
                    CardPutTo(creature.Card, place);
                    break;
            }
        }

        /// <summary>
        /// Запрос текущей маны
        /// </summary>
        public int GetMana(ElementType element) {
            return _mana[element];
        }
        /// <summary>
        /// Увеличение маны
        /// </summary>
        public void IncrementMana(ElementType element, int value) {
            if (GlobalSummary.IsManaIncomeLock(element) || value == 0) return;
            Game.Log.Add(Resources.Game.ManaIncrement, this, element.ManaName, value);
            _mana.Increment(element, value);
            GlobalSummary.DoManaChange(this, element, value);
            _manaStep[element] = 4;
        }
        /// <summary>
        /// Уменьшение маны
        /// </summary>
        public void DecrementMana(ElementType element, int value) {
            if (GlobalSummary.IsManaOutcomeLock(element) || value == 0) return;
            _mana.Decrement(element, value);
            GlobalSummary.DoManaChange(this, element, -value);
            _hasDecMana = true;
        }

        public bool TestMana(ElementValues manas, bool notice) {
            foreach (ElementType element in ElementType.AllElements) {
                if (manas[element] > 0 && manas[element] > _mana[element]) {
                    if (notice) Game.ErrorNotice(ManaCondition.NotEnough.GetText(element));
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Медитация
        /// </summary>
        public void DoMeditation() {
            int v = 1 + _counterMeditation/2;
            Game.Log.Add(Resources.Game.ManaIncrementAll, this, v);
            foreach (ElementType element in ElementType.ManaElements)
                if (!GlobalSummary.IsManaIncomeLock(element))
                    _mana.Increment(element, v);
            _hasMeditation = true;
            _counterMeditation++;
            IncMeditation(v);
            Summary.DoMeditation();
            GlobalSummary.DoMeditation();
        }

        /// <summary>
        /// Добавление очков медитации
        /// </summary>
        public void IncMeditation(int value) {
            int v = value;
            int m = Math.Min(v, Game.Meditation);
            if (m > 0) {
                ChangeMeditation(m);
                v -= m;
            }
            if (v == 0) return;
            m = Math.Min(v, Enemy.Meditation);
            if (m > 0) {
                Enemy.DecMeditation(m);
                ChangeMeditation(m/2);
            }
        }

        /// <summary>
        /// Уменьшение очков медитации
        /// </summary>
        public void DecMeditation(int value) {
            ChangeMeditation(-Math.Min(value, _meditation));
        }

        private void ChangeMeditation(int value) {
            _meditation += value;
            Game.Meditation -= value;
        }

        /// <value>
        /// Было ли в этот ход уменьшение маны
        /// </value>
        public bool HasDecrementMana { get { return _hasDecMana; } }

        private void UpdateMana() {
            int mana, source;
            foreach (ElementType element in ElementType.AllElements) {
                mana = _mana[element];
                source = GlobalSummary.GetManaBalance(element);
                if (source >= 0) {
                    if (mana < source) mana = source;
                } else {
                    source = Math.Abs(source);
                    if (source > mana) {
                        foreach (SummonedObject summon in GetSummonedObjects())
                            if (summon.Summary.GetManaOutcome(element) > 0)
                                summon.ReturnToHand();
                    } else
                        mana -= source;
                }
                _mana[element] = mana;
            }
        }
        private void UpdateHandCards() {
            int i = 0, count = 0, lost = 0;
            while (i < _hands.Count) {
                HandCard card = _hands[i];
                if (card.Opened) {
                    if (count == Constants.MaxOpenedCardCount) {
                        _deck2.Add(card.Card);
                        _hands.RemoveAt(i);
                        lost++;
                        continue;
                    }
                    count++;
                }
                i++;
            }
            if (lost > 0)
                Game.Log.Add(Resources.Game.LoseCard, this, lost);
        }
        private void UpdateDelayed() {
            for (int i = 0; i < _creatures.Length; i++) {
                Creature cr = _creatures[i];
                if (cr != null && cr.State == CreatureState.Delay)
                    cr.State = CreatureState.Untapped;
            }
        }

        public IEnumerable<SummonedObject> GetSummonedObjects() {
            List<SummonedObject> list = new List<SummonedObject>(_lands.Count + Constants.FieldSize);
            for (int i = 0; i < _lands.Count; i++) list.Add(_lands[i]);
            for (int i = 0; i < _creatures.Length; i++) if (_creatures[i] != null) list.Add(_creatures[i]);
            return list;
        }

        public void StartDefence() {
            _state = PlayerState.Defence;
            _hasDecMana = false;
            _hasMeditation = false;
            //Game.Log.Add(Resources.Game.DefencePhase, this);
            UpdateDelayed();
            OnStartDefencePhase();
            Game.UpdateAll();
            Tabs.SelectTab(0);
            HandleChanges();
        }

        public void EndDefence() {
            OnEndDefencePhase();
            HandleChanges();
        }

        public void StartOffence(bool first) {
            _state = PlayerState.Offence;
            _hasDecMana = false;
            _hasMeditation = false;
            //Game.Log.Add(Resources.Game.OffencePhase, this);
            if (!first) {
                UpdateMana();
                CardToHands(false, true);
                OnStartOffencePhase();
                DoUndying();
            }
            Game.UpdateAll();
            Tabs.SelectTab(0);
            HandleChanges();
        }

        public void EndOffence() {
            if (!_hasMeditation) _counterMeditation = 0;
            UpdateHandCards();
            OnEndOffencePhase();
            Game.UpdateAll();
            HandleChanges();
        }

        public override void OnStartOffencePhase() {
            base.OnStartOffencePhase();
            GlobalSummary.DoStartOffencePhase();
            foreach (SummonedObject summon in GetSummonedObjects()) {
                summon.OnStartOffencePhase();
                summon.RemoveEndedAffects();
            }
            RemoveEndedAffects();
        }
        public override void OnEndOffencePhase() {
            base.OnEndOffencePhase();
            GlobalSummary.DoEndOffencePhase();
            foreach (SummonedObject summon in GetSummonedObjects()) {
                summon.OnEndOffencePhase();
                summon.RemoveEndedAffects();
            }
            RemoveEndedAffects();
        }
        public override void OnStartDefencePhase() {
            base.OnStartDefencePhase();
            GlobalSummary.DoStartDefencePhase();
            foreach (SummonedObject summon in GetSummonedObjects()) {
                summon.OnStartDefencePhase();
                summon.RemoveEndedAffects();
            }
            RemoveEndedAffects();
        }
        public override void OnEndDefencePhase() {
            base.OnEndDefencePhase();
            GlobalSummary.DoEndDefencePhase();
            foreach (SummonedObject summon in GetSummonedObjects()) {
                summon.OnEndDefencePhase();
                summon.RemoveEndedAffects();
            }
            RemoveEndedAffects();
        }
        public override void OnBattleReady() {
            foreach (SummonedObject summon in GetSummonedObjects())
                summon.OnBattleReady();
        }

        public void DrawArrows(bool enemy) {
            for (int i = 0; i < Constants.FieldSize; i++) {
                Creature attacker = _creatures[i];
                if (attacker != null && attacker.State == CreatureState.Offence) {
                    for (int j = 0; j < attacker.Defenders.Count; j++)
                        DrawArrow(enemy, attacker.Defenders[j].Number, i);
                }
            }
        }
        private void DrawArrow(bool enemy, int from, int to) {
            Surface arrow = Data.GetAttackArrow(from, to, enemy);
            arrow.Draw(Constants.GetAttackArrowX(from, to), Constants.GetAttackArrowY(enemy));
        }

        public void DrawAvatar(Rectangle rect) {
            if (Avatar != null)
                Avatar.Draw(rect.Location);
            else
                Display.FillRect(rect, Color.Black);
            DrawHealthChange(rect, false);
        }

        public void DrawHealth(bool side) {
            int h = _healthPos;
            if (h <= 0) return;
            if (h <= 20) {
                DrawHealth(side, h, 0);
                return;
            }
            int t = h/100, w = h%100;
            bool c = (t & 1) == 1;
            if (t > 0) DrawHealth(side, 100, c ? 1 : 2);
            if (w > 0) DrawHealth(side, w, c ? 2 : 1);
            Data.HealthBarOverlay.Draw(side ? Constants.HealthBar2X : Constants.HealthBar1X, Constants.HealthBarY);
        }
        private void DrawHealth(bool side, int w, int color) {
            int x = side ? Constants.HealthBar2X : Constants.HealthBar1X;
            if (side) x += 100 - w;
            Rectangle rect = new Rectangle(x, Constants.HealthBarY, w, 7);
            Display.FillRect(rect, Constants.HealthBarColor[color]);
        }

        public void DrawInfo(int x) {
            int centerX = x + Constants.AvatarSize/2;
            Data.Fonts.PlayerName.Color = Color.White;
            Data.Fonts.PlayerName.DrawCenter(centerX, Constants.PlayerNameY, Name.ToString());
        }

        public void DrawMeditation(int side) {
            int m = Meditation, h = 0;
            Rectangle src, dest;
            if (m > 0) {
                if (m <= 17)
                    h = 4 * m;
                else
                    h = 68 + 5 * (m - 17);
                int y = Constants.MeditationHeight - h;
                src = new Rectangle(Constants.MeditationSrcX[side], Constants.MeditationSrcY + y, Constants.MeditationWidth, h);
                dest = new Rectangle(Constants.MeditationX[side], Constants.MeditationY + y, Constants.MeditationWidth, h);
                Data.MeditationBars.Draw(src, dest);
            }
            src = new Rectangle(Constants.MeditationOverlaySrcX[side], Constants.MeditationSrcY, Constants.MeditationWidth, Constants.MeditationHeight);
            dest = new Rectangle(Constants.MeditationX[side], Constants.MeditationY, Constants.MeditationWidth, Constants.MeditationHeight);
            Data.MeditationBars.Draw(src, dest);
            src = new Rectangle(Constants.MeditationMarkSrcX[side], Constants.MeditationMarkSrcY, Constants.MeditationMarkWidth, Constants.MeditationMarkHeight);
            dest = new Rectangle(Constants.MeditationMarkX[side], Constants.MeditationMarkY - 3 * m, Constants.MeditationMarkWidth, Constants.MeditationMarkHeight);
            Data.MeditationBars.Draw(src, dest);
            Data.Fonts.PlayerMana.Color = 0x30a0ff.ToColor();
            Data.Fonts.PlayerMana.DrawCenter(dest.X+Constants.MeditationMarkTextX, dest.Y+Constants.MeditationMarkTextY, m.ToString());
        }

        /// <summary>
        /// Отрисовка информации о манах и источниках
        /// </summary>
        public void DrawManas(int y) {
            Data.Fonts.PlayerMana.Color = Color.Black;
            string s;
            foreach (ElementType element in ElementType.ManaElements) {
                //Data.Fonts.PlayerMana.Color = element.ElementColor;
                s = string.Format("{0}/{1}", _mana[element], GlobalSummary.GetManaBalance(element));
                Data.Fonts.PlayerMana.Draw(Constants.PlayerManaX[element.Number], y, s);
                int step = _manaStep[element];
                if (step > 0) {
                    Data.ManaGain.Color = element.ElementColor;
                    Rectangle dest = new Rectangle(Constants.PlayerManaX[element.Number]-24, y-6, Constants.ManaGainWidth, Constants.ManaGainHeight);
                    int phase = Constants.ManaGainLayer1[step];
                    if (phase >= 0)
                        Data.ManaGain.Draw(Constants.ManaGainRects[phase], dest);
                    phase = Constants.ManaGainLayer2[step];
                    if (phase >= 0)
                        Data.ManaGain.Draw(Constants.ManaGainRects[phase], dest);
                    _manaStep.Decrement(element);
                }
            }
        }

        public void Process() {
            ProcessHealthChange();
            ProcessHealthBar();
        }

        private void ProcessHealthBar() {
            _healthPos += Math.Sign(Health*5 - _healthPos);
        }

        public void UpdateTabs() {
            ITabSource source = GetTabSource();
            if (_tabs == null) {
                _tabs = new Tabs(this, source);
                _newTabs = null;
                ShowTabs();
                return;
            }
            if (_tabs.CompareSource(this, source)) {
                if (_newTabs != null) {
                    _tabs.HideEvent -= OnTabsHide;
                    _newTabs = null;
                }
                ShowTabs();
                return;
            }
            if (_newTabs != null) {
                if (_newTabs.CompareSource(this, source)) return;
                _tabs.HideEvent -= OnTabsHide;
            }
            _newTabs = new Tabs(this, source);
            if (_newTabs.Count == 0) {
                _newTabs = null;
                ShowTabs();
                return;
            }
            _tabs.HideEvent += OnTabsHide;
            _tabs.Hide();
        }
        private void OnTabsHide() {
            _tabs.HideEvent -= OnTabsHide;
            _tabs.Current.Content.OnDeselect();
            if (_newTabs != null) {
                _tabs = _newTabs;
                _newTabs = null;
                ShowTabs();
                Game.UpdateRegions();
            }
        }
        private void ShowTabs() {
            _tabs.Show(DefaultTab);
        }

        private ITabSource GetTabSource() {
            ITabSource source;
            if (_selectedCreature != null) {
                source = _selectedCreature;
                if (TabSourceNotEmpty(source)) return source;
            }
            if (_selectedLand != null) {
                source = _selectedLand;
                if (TabSourceNotEmpty(source)) return source;
            }
            source = _selectedPlayer;
            if (TabSourceNotEmpty(source)) return source;
            return this;
        }

        private bool TabSourceNotEmpty(ITabSource source) {
            bool[] result = source.GetConditions(this);
            for (int i = 0; i < result.Length; i++) if (result[i]) return true;
            return false;
        }

        #region ITabSource methods
        public bool[] GetConditions(Player player) {
            bool[] result = new bool[5];
            result[0] = player == this && _hands.Count > 0;
            result[1] = player == this && GetAbilities().Count > 0;
            result[2] = _lands.Count > 0;
            result[3] = Summary.GetEnchantments().Count != 0;
            result[4] = _grave.Count > 0;
            return result;
        }
        public IEnumerable<TabContent> GetTabs(Player player) {
            bool[] result = GetConditions(player);
            if (result[0]) yield return PlayerHandsTabContent.GetContent(_tabs, this);
            if (result[1]) yield return AbilitiesTabContent.GetContent(_tabs, player, this);
            if (result[2]) yield return PlayerLandsTabContent.GetContent(_tabs, player, this);
            if (result[3]) yield return EnchantmentsTabContent.GetContent(_tabs, this);
            if (result[4]) yield return PlayerGraveyardTabContent.GetContent(_tabs, this);
        }
        #endregion

        /// <summary>
        /// Запрос списка регионов для данного игрока
        /// </summary>
        public IEnumerable<Region> GetRegions() {
            if (_regions == null) UpdateRegions();
            return _regions;
        }
        public void UpdateRegions() {
            if (_regions == null)
                _regions = new List<Region>();
            else
                _regions.Clear();
            UpdateRegionsCreature(this);
            UpdateRegionsCreature(Enemy);
        }
        private void UpdateRegionsCreature(Player owner) {
            foreach (Creature cr in owner.GetCreatures())
                _regions.Add(new PlayerCreatureRegion(this, cr));
        }

        public override Region FindRegion() {
            return Game.FindRegionBy(this);
        }
    }

    /// <summary>
    /// Регион, определяющий призванное существо игрока
    /// </summary>
    public class PlayerCreatureRegion: HelpRegion {
        private Player _player;
        private Creature _creature;
        private AffectIconHint _hint;

        public PlayerCreatureRegion(Player player, Creature creature):
        base(Constants.GetRowRect(creature.Number, creature.State == CreatureState.Offence ? 1 : (player == creature.Owner ? 2 : 0))) {
            _player = player;
            _creature = creature;
            _hint = new AffectIconHint();
        }

        public Creature Creature { get { return _creature; } }
        public bool Selected {
            get { return _player.SelectedCreature == Creature; }
            set {
                if (value)
                    _player.SelectCreature(Creature);
                else
                    _player.ResetSelection();
            }
        }

        public bool IsEnemy { get { return _player != _creature.Owner; } }

        private Rectangle MovedRect {
            get {
                Rectangle rect = Rect;
                if (IsEnemy)
                    rect.Y += _creature.ForwardStep;
                else
                    rect.Y -= _creature.ForwardStep;
                return rect;
            }
        }

        public override Hint GetHint() {
            _creature.AffectIcons.Update(_creature.Summary.Icons, MovedRect.Location);
            return _hint.GetHint(_creature.AffectIcons.FindByMouse());
        }

        public override void Draw() {
            Rectangle rect = MovedRect;
            _creature.Draw(rect);
            if (Selected)
                Data.CardSelect2.Draw(rect.Location);
        }

        public override void DrawTarget() {
            Draw();
            Data.CardSelect1.Draw(MovedRect.Location);
        }

        public override void OnClick() {
            Selected = !Selected;
            _player.UpdateTabs();
            Game.Current.UpdateRegions();
        }

        public override void OnDoubleClick() {
            if (IsEnemy)
                return;
            if (_player.State != PlayerState.Offence || !_player.IsCurrent)
                return;
            if (!_creature.CanChangeAttack)
                return;
            _player.Game.Mode.AddEvent(new Events.CreatureAction(new AttackTargetArgs(_player, _creature)));
            _player.ResetSelection();
        }

        public override void OnProcess() {
            _creature.Process();
        }

        public override IHelpProvider HelpObject {
            get { return _creature.Card; }
        }
    }
}
